﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace DecisionsFramework.Design.Properties
{
    /// <summary>
    /// Translate attribute allows you to show object data in categorized sections.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public class PropertyClassificationAttribute : Attribute
    {
        private string[] node;
        private string overridePropertyName;
        private int order;

        #region .ctors

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyClassificationAttribute"/> class.
        /// </summary>
        /// <param name="node">Specifies the top-level node where this property will be shown in the context editor.</param>
        public PropertyClassificationAttribute(string node)
            : this(new string[] { node })
        { }

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyClassificationAttribute"/> class.
        /// </summary>
        /// <param name="node">Specifies the path to the node where this property will be shown in the context editor.</param>
        public PropertyClassificationAttribute(string[] node)
        {
            if ((node == null) || (node.Length == 0))
                throw new ArgumentNullException("node");
            for (int index = 0; index < node.Length; index++)
            {
                if (String.IsNullOrEmpty(node[index]) == true)
                    throw new ArgumentException("Property category node can't have null or empty path elements.", "node");
            }

            this.node = node;
        }



        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyClassificationAttribute"/> class.
        /// </summary>
        /// <param name="node">Specifies the path to the node where this property will be shown in the context editor.</param>
        /// <param name="overridePropertyName">Specifies the label for this property that will be displayed in context editor.</param>
        /// <param name="order">Specifies an Int32 value that will be used by context editor to sort properties that are under the same node.</param>
        public PropertyClassificationAttribute(string[] node, string overridePropertyName, int order)
            : this(node)
        {
            if (String.IsNullOrEmpty(overridePropertyName) == true)
                throw new ArgumentNullException("overridePropertyName");

            this.overridePropertyName = overridePropertyName;
            this.order = order;
        }

        public PropertyClassificationAttribute(string[] node, string overridePropertyName, int order, bool isHidden)
            : this(node)
        {
            if (isHidden == false)
            {
                if (String.IsNullOrEmpty(overridePropertyName) == true)
                    throw new ArgumentNullException("overridePropertyName");

                this.overridePropertyName = overridePropertyName;

            }
            this.order = order;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyClassificationAttribute"/> class.
        /// </summary>
        /// <param name="overridePropertyName">Specifies the label for this property that will be displayed in context editor.</param>
        /// <param name="order">Specifies an Int32 value that will be used by context editor to sort properties that are under the same node.</param>
        public PropertyClassificationAttribute(string overridePropertyName, int order)
        {
            if (String.IsNullOrEmpty(overridePropertyName) == true)
                throw new ArgumentNullException("overridePropertyName");

            this.overridePropertyName = overridePropertyName;
            this.order = order;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyClassificationAttribute"/> class.
        /// </summary>
        /// <param name="node">Specifies the path to the node where this property will be shown in the context editor.</param>
        /// <param name="order">Specifies an Int32 value that will be used by context editor to sort properties that are under the same node.</param>
        public PropertyClassificationAttribute(string[] node, int order)
            : this(node)
        {
            this.order = order;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyClassificationAttribute"/> class.
        /// </summary>
        /// <param name="order">Specifies an Int32 value that will be used by context editor to sort properties that are under the same node.</param>
        /// <param name="overridePropertyName">Specifies the label for this property that will be displayed in context editor.</param>
        /// <param name="node">Specifies the path to the node where this property will be shown in the context editor.</param>
        public PropertyClassificationAttribute(int order, string overridePropertyName, params string[] node)
            : this(node, overridePropertyName, order)
        { }

        #endregion

        /// <summary>
        /// Gets the path of the node where this property will be shown in context editor.
        /// </summary>
        /// <remarks>Can be null or empty. In this case the property should appear in the default node.</remarks>
        public string[] Node
        {
            get { return this.node; }
        }

        /// <summary>
        /// Get the label that will be displayed in context editor for this property.
        /// </summary>
        /// <remarks>Can be null or empty. In this case the actual property name should be used.</remarks>
        public virtual string OverridePropertyName
        {
            get { return this.overridePropertyName; }
        }

        /// <summary>
        /// Gets an Int32 value that will be used by context editor to order properties that are in the same node.
        /// </summary>
        public int Order
        {
            get { return this.order; }
        }

        #region Static Helpers

        /// <summary>
        /// Gets the <see cref="PropertyClassificationAttribute"/> attribute instance that is set for the specified member info.
        /// </summary>
        /// <param name="mi">The member that should have the attribute.</param>
        /// <returns>The attribute instance that is set for the specified member or null if the specified member doesn't have a <see cref="PropertyClassificationAttribute"/> attribute set.</returns>
        public static PropertyClassificationAttribute Of(MemberInfo mi)
        {
            Attribute[] attributes = Attribute.GetCustomAttributes(mi, typeof(PropertyClassificationAttribute), true);
            if ((attributes != null) && (attributes.Length > 0))
            {
                return (attributes[0] as PropertyClassificationAttribute);
            }
            return null;
        }

        #endregion
    }
}
